package com.duapk.index.work;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.com.bytecode.opencsv.CSVReader;

import com.duapk.index.bean.FieldInfo;
import com.duapk.index.cache.ResourceCache;
import com.duapk.index.constant.Constant;
import com.duapk.index.util.StringTools;
import com.duapk.nlp.analyzer.ApkAnalyzer;
import com.duapk.nlp.query.ApkSimilarity;
import com.duapk.nlp.util.StringUtils;

/**
 * 索引创建线程
 * 
 * @Package: com.duapk.index.work
 * @ClassName: IndexWork
 * @author: jiangzheng
 * @date: Apr 4, 2011 5:14:28 PM
 * 
 */
public class IndexWork implements Runnable {

	private static final String CATEGORY = "Category";

	private static final String SIZE = "Size";

	private static final String DETAILS = "Details";

	private static final String APK_NAME = "ApkName";

	private static final String DOWNLOAD_URL = "DownloadUrl";

	private static final String URL = "Url";

	private static final String BRIEF = "Brief";

	private static final String PUBTIME = "Pubtime";

	private static final String AUTHOR = "Author";

	private static final String VERSION = "Version";

	private static final String NAME = "Name";

	private static final String NAME_ID = "NameID";

	private static final String FULL_NAME = "FullName";

	private static final String LOGO = "Logo";

	private static final String STAR = "Star";

	private static Logger logger = LoggerFactory.getLogger(IndexWork.class);

	private static ResourceCache cache = ResourceCache.getInstance();

	private Map<String, Field> fieldMap;

	private Map<String, NumericField> numericFieldMap;

	private static ApkSimilarity similarity = new ApkSimilarity();

	private static Analyzer analyzer;

	private List<File> csvSourceFiles;

	private String category;

	private List<Integer> finishedJobs;

	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-d");

	static {
		try {
			analyzer = new ApkAnalyzer();
		} catch (IOException e) {
			System.err
					.println("Fatal Error: fail to create AspireAnalyzer. error message is "
							+ e.getMessage());
			System.exit(1);
		}

		List<FieldInfo> fieldInfos = cache.getFieldInfos();
		for (FieldInfo fieldInfo : fieldInfos) {
			similarity.setLengthNormFactor(fieldInfo.getName(), fieldInfo
					.getMinTerms(), fieldInfo.getMaxTerms());
		}
	}

	public IndexWork(List<File> csvSourceFiles, String category,
			List<Integer> finishedJobs) {
		this.finishedJobs = finishedJobs;
		this.csvSourceFiles = csvSourceFiles;
		this.category = category;
		createIndexFields();
	}

	public void run() {
		IndexWriter writer = null;
		// 读取CSV文件创建索引
		try {
			Directory dir = FSDirectory.open(new File(Constant.INDEX_PATH
					+ File.separator + category));
			IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_32,
					analyzer);
			config.setRAMBufferSizeMB(64).setSimilarity(similarity).setOpenMode(OpenMode.CREATE);
			writer = new IndexWriter(dir, config);

			for (File csvSource : csvSourceFiles) {
				CSVReader reader = null;
				try {
					reader = new CSVReader(new InputStreamReader(
							new FileInputStream(csvSource), "UTF-8"));

					String[] cols = null;
					int index = 0;
					while ((cols = reader.readNext()) != null) {
						if (index == 0 || cols.length < 11) {
							index++;
							continue;
						}
						Document doc = createDocument(cols, csvSource.getName()
								.replace(".csv", ""));
						writer.addDocument(doc);
					}
				} catch (IOException e1) {
					logger.error("create index error, csvFile is {}", csvSource
							.getAbsoluteFile(), e1);
				} finally {
					if (reader != null) {
						try {
							reader.close();
						} catch (IOException e1) {
							logger.error("close CsvReader error.", e1);
						}
					}
				}

			}
			writer.commit();
			writer.optimize();
		} catch (IOException e) {
			logger.error("create IndexWriter error.", e);
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					logger.error("close IndexWriter error.", e);
				}
			}
			synchronized (finishedJobs) {
				finishedJobs.add(1);
				finishedJobs.notify();
			}
		}
	}

	/**
	 * 生成索引文档
	 * 
	 * @Title: createDocument
	 * @author: jiangzheng
	 * @date: Apr 4, 2011 6:15:15 PM
	 * @param cols
	 * @return
	 */
	private Document createDocument(String[] cols, String category) {
		Document doc = new Document();
		addField(doc, NAME, cols[0]);
		addField(doc, NAME_ID, StringTools.getHashValue(cols[0]), DataType.INT);
		addField(doc, FULL_NAME, StringUtils.transfer(cols[0]));
		addField(doc, VERSION, StringTools.parseFloat(cols[1], 0),
				DataType.FLOAT);
		addField(doc, AUTHOR, cols[2]);
		addField(doc, PUBTIME, StringTools.parseTime(cols[3], sdf, System
				.currentTimeMillis()), DataType.LONG);
		addField(doc, BRIEF, cols[4]);
		addField(doc, URL, cols[5]);
		addField(doc, DOWNLOAD_URL, cols[6]);
		addField(doc, APK_NAME, cols[7]);
		addField(doc, DETAILS, cols[8]);
		addField(doc, SIZE, StringTools.parseInt(cols[9], 0), DataType.INT);
		addField(doc, LOGO, cols[10]);
		addField(doc, STAR, StringTools.parseFloat(cols.length >= 12 ? cols[11]
				: "", 0), DataType.FLOAT);
		addField(doc, CATEGORY, category);
		return doc;
	}

	/**
	 * 添加字段至文档中
	 * 
	 * @Title: addField
	 * @author: jiangzheng
	 * @date: Apr 11, 2011 6:02:58 PM
	 * @param doc
	 * @param fieldName
	 * @param value
	 */
	private void addField(Document doc, String fieldName, String value) {
		Field field = null;
		if (fieldMap.containsKey(fieldName)) {
			field = fieldMap.get(fieldName);
		} else {
			field = new Field(fieldName, "", Field.Store.YES,
					Field.Index.ANALYZED);
		}
		field.setValue(value);
		doc.add(field);
	}

	/**
	 * 添加字段至文档中
	 * 
	 * @Title: addNumericField
	 * @author: jiangzheng
	 * @date: May 29, 2011 4:34:32 PM
	 * @param doc
	 * @param fieldName
	 */
	private void addField(Document doc, String fieldName, Number number,
			DataType dataType) {
		NumericField field = null;
		if (numericFieldMap.containsKey(fieldName)) {
			field = numericFieldMap.get(fieldName);
		} else {
			field = new NumericField(fieldName, Field.Store.YES, true);
		}
		switch (dataType) {
		case INT:
			field.setIntValue(number.intValue());
			break;
		case LONG:
			field.setLongValue(number.longValue());
			break;
		case FLOAT:
			field.setFloatValue(number.floatValue());
			break;
		case DOUBLE:
			field.setDoubleValue(number.doubleValue());
			break;
		default:
			break;
		}
		doc.add(field);
	}

	/**
	 * 创建索引字段
	 * 
	 * @Title: createIndexFields
	 * @author: jiangzheng
	 * @date: Apr 4, 2011 5:38:59 PM
	 * @return
	 */
	private void createIndexFields() {
		fieldMap = new HashMap<String, Field>();
		numericFieldMap = new TreeMap<String, NumericField>();
		List<FieldInfo> fieldInfos = cache.getFieldInfos();
		for (FieldInfo fieldInfo : fieldInfos) {
			if ("numeric".equals(fieldInfo.getType())) {
				NumericField field = new NumericField(fieldInfo.getName(),
						fieldInfo.getStore(), true);
				numericFieldMap.put(fieldInfo.getName(), field);
			} else {
				Field field = new Field(fieldInfo.getName(), "", fieldInfo
						.getStore(), fieldInfo.getIndex());
				field.setBoost(fieldInfo.getBoost());
				fieldMap.put(fieldInfo.getName(), field);
			}

		}
	}

	/**
	 * 数据类型
	 * 
	 * @Package: com.duapk.index.work
	 * @ClassName: DataType
	 * @author: jiangzheng
	 * @date: May 29, 2011 4:37:11 PM
	 * 
	 */
	private static enum DataType {
		INT, LONG, FLOAT, DOUBLE
	}
}
